/**
 * sysmets4.c -- System Metrics Display Program No. 4
 *              (c) Charles Petzold, 1998
 */

 #include <windows.h>
 #include "../04/sysmets.h"
 
 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 
 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
 {
     static TCHAR szAppName[] = TEXT("SysMets4");
     HWND hwnd;
     MSG msg;
     WNDCLASS wndclass;
 
     wndclass.style = CS_HREDRAW | CS_VREDRAW;
     wndclass.lpfnWndProc = WndProc;
     wndclass.cbClsExtra = 0;
     wndclass.cbWndExtra = 0;
     wndclass.hInstance = hInstance;
     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
     wndclass.lpszMenuName = NULL;
     wndclass.lpszClassName = szAppName;
 
     if (!RegisterClass(&wndclass))
     {
         MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
         return 0;
     }
 
     hwnd = CreateWindow(szAppName,
                         TEXT("Get System Metrics No. 4"),
                         WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         480,
                         480,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);
     ShowWindow(hwnd, iCmdShow);
     UpdateWindow(hwnd);
 
     while (GetMessage(&msg, NULL, 0, 0))
     {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
     }
     return msg.wParam;
 }
 
 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     static int cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth;
     HDC hdc;
     int i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;
     PAINTSTRUCT ps;
     SCROLLINFO si;
     TCHAR szBuffer[10];
     TEXTMETRIC tm;
 
     switch (message)
     {
     case WM_CREATE:
         hdc = GetDC(hwnd);
 
         GetTextMetrics(hdc, &tm);
         cxChar = tm.tmAveCharWidth;
         cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
         cyChar = tm.tmHeight + tm.tmExternalLeading;
         ReleaseDC(hwnd, hdc);
 
         // Save the width of the three columns;
         iMaxWidth = 40*cxChar+22*cxCaps;
         return 0;
 
     case WM_SIZE:
         cxClient = LOWORD(lParam);
         cyClient = HIWORD(lParam);
         // Set vertical scroll bar range an page size
         si.cbSize = sizeof(si);
         si.fMask = SIF_RANGE | SIF_PAGE;
 
         si.nMin = 0;
         si.nMax = NUMLINES -1;
         si.nPage = cyClient / cyChar;
         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
         // Set horizontal scroll bar range and page size
         si.cbSize = sizeof(si);
         si.fMask = SIF_RANGE | SIF_PAGE;
         si.nMin = 0;
         si.nMax = 2 + iMaxWidth / cxChar;
         si.nPage = cxClient / cxChar;
         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
         return 0;
 
     case WM_VSCROLL:
         // Get all the vertical scroll bar information
         si.cbSize = sizeof(si);
         si.fMask = SIF_ALL;
         GetScrollInfo(hwnd, SB_VERT, &si);
         // Save th position for comparison later on
         iVertPos = si.nPos;
 
         switch (LOWORD(wParam))
         {
             case SB_TOP: si.nPos = si.nMin; break;
             case SB_BOTTOM: si.nPos = si.nMax; break;
             case SB_LINEUP: si.nPos -= 1; break;
             case SB_LINEDOWN: si.nPos += 1; break;
             case SB_PAGEUP: si.nPos -= si.nPage; break;
             case SB_PAGEDOWN: si.nPos += si.nPage; break;
             case SB_THUMBTRACK:  si.nPos = si.nTrackPos; break;
             default: break;
         }
         // Set the position and then retrieve it. Due to adjustments
         // by Windows it may not be the same as the value set.
         si.fMask = SIF_POS;
         SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
         GetScrollInfo(hwnd, SB_VERT, &si);
         // If the positon has changed, scroll th window and update it
         if (si.nPos != iVertPos) {
             ScrollWindow(hwnd, 0, cyChar *(iVertPos-si.nPos), NULL, NULL);
             UpdateWindow(hwnd);
         }
         return 0;
     case WM_HSCROLL:
         // Get all the vertical scroll bar information
         si.cbSize = sizeof(si);
         si.fMask = SIF_ALL;
         // Save the position for comparsion later on
         GetScrollInfo(hwnd, SB_HORZ, &si);
         iHorzPos = si.nPos;
         switch (LOWORD(wParam))
         {
             case SB_LINELEFT: si.nPos -= 1; break;
             case SB_LINERIGHT: si.nPos += 1; break;
             case SB_PAGELEFT: si.nPos -= si.nPage; break;
             case SB_PAGERIGHT: si.nPos += si.nPage; break;
             case SB_THUMBPOSITION: si.nPos = si.nTrackPos; break;
             default: break;
         }
         // Set the position and then retrieve it. Due to adjustments
         // by Windows it may not be the same as the value set.
         si.fMask = SIF_POS;
         SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
         GetScrollInfo(hwnd, SB_HORZ, &si);
         // If the position has changed, scroll the window
         if (si.nPos != iHorzPos) {
             ScrollWindow(hwnd, cxChar *(iHorzPos-si.nPos), 0, NULL, NULL);
         }
         return 0;
     case WM_KEYDOWN:
         switch (wParam) {
            case VK_HOME:  SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0); break;
            case VK_END :  SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0); break;
            case VK_PRIOR: SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); break;
            case VK_NEXT:  SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); break;
            case VK_UP:    SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); break;
            case VK_DOWN:  SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); break;
            case VK_LEFT:  SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0); break;
            case VK_RIGHT: SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0); break;
         }
         return 0;
     case WM_PAINT:
         hdc = BeginPaint(hwnd, &ps);
         // Get vertical scroll bar position
         si.cbSize = sizeof(si);
         si.fMask = SIF_POS;
         GetScrollInfo(hwnd, SB_VERT, &si);
         iVertPos = si.nPos;
         // Get horizontal scroll bar position
         GetScrollInfo(hwnd, SB_HORZ, &si);
         iHorzPos = si.nPos;
         // Find painting limits
         iPaintBeg = max(0, iVertPos + ps.rcPaint.top/ cyChar);
         iPaintEnd = min(NUMLINES -1, iVertPos + ps.rcPaint.bottom / cyChar);
 
         for(i=iPaintBeg; i<=iPaintEnd; i++) {
             x = cxChar * (1-iHorzPos);
             y = cyChar * (i-iVertPos);
             
             TextOut(hdc, x, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));
             TextOut(hdc, x+22*cxCaps,y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));
             SetTextAlign(hdc, TA_RIGHT | TA_TOP);
             TextOut(hdc, x+22*cxCaps + 40*cxChar, y, szBuffer, 
                 wsprintf(szBuffer, TEXT("%5d"),GetSystemMetrics(sysmetrics[i].iIndex)));
             SetTextAlign(hdc, TA_LEFT | TA_TOP);
         }
         EndPaint(hwnd, &ps);
         return 0;
 
     case WM_DESTROY:
         PostQuitMessage(0);
         return 0;
     }
     return DefWindowProc(hwnd, message, wParam, lParam);
 }